"""
CSPBuilder plugin

Enables broadcast on channels. If a channel is marked with broadcast, it will be replaced by a One2All Process.

A One2All Channel implementation could easily cause deadlocks. To Finish a send, all targets must have received exactly one packet.

Creating a One2All Process, that does a Parallel send to all, will eliminate the possibility of deadlocks and can be implemented with the current feature set, by using One2One channels.

Copyright (c) 2008 Rune M. Friborg, runef@diku.dk.
See LICENSE.txt for licensing details (GPLv2).
"""

PLUGIN_NAME = "Broadcast"

import wx
from plugins.builder import *

# DrawObject type IDs:

obj_CHANNEL          = 1
obj_PROCESS          = 2
obj_CPOINT           = 3
obj_INPUTCONNECTION  = 4
obj_OUTPUTCONNECTION = 5


class PluginBroadcast(PluginBUILDER):

    def __init__(self, pluginManager):
        PluginBUILDER.__init__(self, pluginManager, PLUGIN_NAME)


    def _rcmenu_activate(self, frame, event):
        """ Enable / Disable broadcast.
        """

        obj = frame.selection[0]

        if obj.plugin_attr.has_key('broadcast') and self._check_broadcast(obj.plugin_attr['broadcast']):
            new_value = 'no'
        else:
            new_value = 'yes'

        obj.plugin_attr['broadcast'] = new_value
        
        toggleList = frame.findObjList(obj, [obj])
        for neighbour in toggleList:
            if neighbour.getObjType() == obj_CPOINT:
                neighbour.plugin_attr['broadcast'] = new_value

        frame.drawPanel.Refresh()


    def _rcmenu_cpoint_broadcast(self, frame, menu, obj, menuid):
        if obj.plugin_attr.has_key('broadcast') and self._check_broadcast(obj.plugin_attr['broadcast']):
            menu.Append(menuid, 'Broadcast [ enabled ]')
            frame.Bind(wx.EVT_MENU, lambda event:self._rcmenu_activate(frame,event), id=menuid)
        else:
            menu.Append(menuid, 'Broadcast [ ]')
            frame.Bind(wx.EVT_MENU, lambda event:self._rcmenu_activate(frame,event), id=menuid)


    def _check_broadcast(self, val):
        return (val == 'yes')


    def _draw_broadcast2(self, dc, obj, position, selected):
        if obj.plugin_attr.has_key('broadcast') and self._check_broadcast(obj.plugin_attr['broadcast']):            
            dc.SetBrush(wx.BLUE_BRUSH)

    def _draw_broadcast3(self, dc, obj, position, selected):
        if obj.plugin_attr.has_key('broadcast') and self._check_broadcast(obj.plugin_attr['broadcast']):            
            dc.SetBrush(wx.BLACK_BRUSH)
 
    def _propagate_broadcast(self, src_obj, dst_obj):
        if src_obj.getObjType() == obj_CPOINT and dst_obj.getObjType() == obj_CPOINT:
            if src_obj.plugin_attr.has_key('broadcast') and self._check_broadcast(src_obj.plugin_attr['broadcast']):            
                dst_obj.plugin_attr['broadcast'] = 'yes'


def load(pluginManager):

    p = PluginBroadcast(pluginManager)

    p.addRightClickElement(location_CPOINT, p._rcmenu_cpoint_broadcast)
    p.addToCreateConnection(p._propagate_broadcast)

    p.addCSPAttribute(node_CPOINT, 'broadcast', str, 'no', p._check_broadcast)
    # Set blue
    p.addDrawElement(location_CPOINT, 2, p._draw_broadcast2)
    # Reset to black
    p.addDrawElement(location_CPOINT, 3, p._draw_broadcast3)

    pluginManager.addPlugin(p)
